// UnitTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

struct Point2D
{
	int x;
	int y;

	int getX() { return x; }
	const int getY() const { return y; }

	void setX(const int x) { this->x = x; }
	void setY(int y) { this->y = y; }
};

struct Point3D
{
	Point2D pt;
	int z;

	void set2D(const Point2D& pt) { this->pt = pt; }
	void setZ(int z) { this->z = z; }
};

struct LinkedList
{
	float value;
	LinkedList* next;

	LinkedList() : value(), next(nullptr) {}

	void setNext(LinkedList* next)
	{
		this->next = next;
	}
};

struct UnitProto
{
	std::string name;
};

struct Unit
{
	UnitProto* definition;
	Point2D position;

	void setDefinition(UnitProto* def) { definition = def; }
	UnitProto* getDefinition() const { return definition; }
};

void RegisterClasses()
{
	MOE::ClassMeta& p3d = MOE::ObjectTypeManager::GetSingleton().RegisterClass<Point3D>(L"Point3D");
	p3d
		<< MOE::Assign::ValueSetter(L"z", &Point3D::setZ)
		<< MOE::Assign::Value(L"2d", &Point3D::pt);

	MOE::ClassMeta& p2d = MOE::ObjectTypeManager::GetSingleton().RegisterClass<Point2D>(L"Point2D");
	p2d
		<< MOE::Assign::Value(L"x", &Point2D::x)
		<< MOE::Assign::ValueAccessors(L"y", &Point2D::getY, &Point2D::setY);

	MOE::ClassMeta& unit = MOE::ObjectTypeManager::GetSingleton().RegisterClass<Unit>(L"Unit");
	unit
		<< MOE::Assign::PointerAccessors(L"def", &Unit::getDefinition, &Unit::setDefinition)
		<< MOE::Assign::Value(L"position", &Unit::position);

	MOE::ClassMeta& uproto = MOE::ObjectTypeManager::GetSingleton().RegisterClass<UnitProto>(L"UnitProto");
	uproto
		<< MOE::Assign::Value(L"name", &UnitProto::name);
	uproto.DefineConstructor(MOE::Assign::DefaultCtor<UnitProto>()).DefineDestructor(MOE::Assign::Dtor<UnitProto>());

	MOE::ClassMeta& linkedlist = MOE::ObjectTypeManager::GetSingleton().RegisterClass<LinkedList>(L"LinkedList");
	linkedlist
		<< MOE::Assign::Value(L"value", &LinkedList::value)
		<< MOE::Assign::PointerSetter(L"next", &LinkedList::setNext);

	// Initialize namespace components.
	MOE::Name::Namespace::SetObjectToNameQuery(boost::make_shared<MOE::Name::MapObjectToNameQuery>());
}

void PointTest()
{
	MOE::ClassMeta& p2d = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"Point2D");
	MOE::ClassMeta& p3d = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"Point3D");

	Point2D testp2d;

	int x = 50;
	int y = 400;

	p2d.Set(&testp2d, L"x", &x);
	p2d.Set(&testp2d, L"y", &y);

	std::cout << testp2d.x << " " << testp2d.y << std::endl;

	int* xPtr;

	p2d.Access(&testp2d, L"x", &xPtr);

	*xPtr = 18;

	Point3D testp3d;

	testp3d.z = 5;
	p3d.Set(&testp3d, L"2d", &testp2d);

	std::cout << testp3d.pt.x << " " << testp3d.pt.y << " " << testp3d.z << std::endl;

	MOE::Persist::PropertyTreeWriter<boost::property_tree::wptree> treeWriter;

	boost::property_tree::wptree outTree2d;
	treeWriter.Write(p2d, &testp2d, outTree2d);

	std::cout << outTree2d.get<int>(L"x", 0) << " " << outTree2d.get<int>(L"y", 0) << std::endl;

	boost::property_tree::wptree outTree3d;
	treeWriter.Write(p3d, &testp3d, outTree3d);

	// Note: since the property "z" doesn't have a getter, the tree write will fail to write the z component and thus will show 0.
	std::cout << outTree3d.get<int>(L"2d.x", 0) << " " << outTree3d.get<int>(L"2d.y", 0) <<  " " << outTree3d.get<int>(L"z", 0) << std::endl;

	boost::property_tree::json_parser::write_json("testp2d.json", outTree2d);
	boost::property_tree::json_parser::write_json("testp3d.json", outTree3d);
}

void UnitProtoTest()
{
	MOE::ClassMeta& unit = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"Unit");
	MOE::ClassMeta& uproto = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"UnitProto");

	UnitProto heroManProto;
	heroManProto.name = "HeroMan";

	UnitProto* pHeroManProto = &heroManProto;

	Point2D position;
	position.x = 5;
	position.y = 10;

	Unit heroMan;

	unit.Set(&heroMan, L"def", &pHeroManProto);
	unit.Set(&heroMan, L"position", &position);

	std::cout << heroMan.position.x << " " << heroMan.position.y << " " << heroMan.definition->name << std::endl;

	UnitProto* pThatProto;

	unit.Get(&heroMan, L"def", &pThatProto);

	std::cout << pThatProto->name << std::endl;

	MOE::Name::Namespace* unitNamespace = MOE::Name::Namespace::GetGlobalNamespace().CreateChildNamespace(MOE::Name::NamePath(L"Units"));
	unitNamespace->SetObject<UnitProto>(MOE::Name::NamePath(L"HeroMan_Def"), &heroManProto);
	unitNamespace->SetObject<Unit>(MOE::Name::NamePath(L"HeroMan"), &heroMan);

	MOE::Persist::NamespacePropertyTreeWriter<boost::property_tree::wptree> namespaceWriter;

	boost::property_tree::wptree outTree;
	namespaceWriter.Write(outTree, unitNamespace);

	boost::property_tree::json_parser::write_json("Units.json", outTree);
}

void JsonReadTest()
{
	MOE::Name::Namespace::GetGlobalNamespace().RemoveAll();

	MOE::ClassMeta& p2d = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"Point2D");
	MOE::ClassMeta& p3d = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"Point3D");
	MOE::ClassMeta& unit = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"Unit");
	MOE::ClassMeta& uproto = MOE::ObjectTypeManager::GetSingleton().GetClassMeta(L"UnitProto");

	boost::property_tree::wptree inTree2d, inTree3d, inTreeUnits;

	boost::property_tree::json_parser::read_json("testp2d.json", inTree2d);
	boost::property_tree::json_parser::read_json("testp3d.json", inTree3d);
	boost::property_tree::json_parser::read_json("Units.json", inTreeUnits);

	MOE::Persist::PropertyTreeReader<boost::property_tree::wptree> treeReader;

	Point2D target2d;
	Point3D target3d;

	treeReader.Read(p2d, inTree2d, &target2d);
	treeReader.Read(p3d, inTree3d, &target3d);

	std::cout << target2d.x << " " << target2d.y << std::endl;
	std::cout << target3d.pt.x << " " << target3d.pt.y << " " << target3d.z << std::endl;

	MOE::Persist::NamespacePropertyTreeReader<boost::property_tree::wptree> namespaceReader;
	namespaceReader.Read(inTreeUnits);

	MOE::Name::Namespace* unitNamespace = MOE::Name::Namespace::GetGlobalNamespace().GetChildNamespace(MOE::Name::NamePath(L"Units"));
	Unit* pTargetUnit = unitNamespace->GetObject<Unit>(MOE::Name::NamePath(L"HeroMan"));
	UnitProto* pTargetUnitProto = unitNamespace->GetObject<UnitProto>(MOE::Name::NamePath(L"HeroMan_Def"));

	std::cout << pTargetUnit->position.x << " " << pTargetUnit->position.y << " " << pTargetUnit->definition->name << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	RegisterClasses();

	PointTest();
	UnitProtoTest();

	JsonReadTest();

	return 0;
}
